<?php
/**
 * app5.5.0以上版本 消息提醒模块
 * Created by PhpStorm.
 * User: 汪志宇
 * Date: 2017/3/6
 * Time: 13:51
 */
namespace soa\remind;

use soa\client;
use artisan\cache;

class Remind extends client
{
    private $cache;
    private $key = 'untreated:msg';
    private $hashKey = ''; 
    private $type = array("uid","wsid");

    //订单、留言、通知、理赔、定时短信、免费短信领取、代扣订单、C端扫码签收 驿站 打印订单自动发短信 微商快递员发送协议申请  微商下线给微商发送授权申请  红包  微商二级用户发送申请 二级用户消息 微商申请解除协议接口 巴枪扫描上传错误  急速扫描服务
    private $MType = ['order','liuyan','notice','indemnity','smsTiming','freeSms','finance','scanCode','dak','orderAutoSms',"agreement","WsAgreement","printRedPack","wsProtocol","pProtocol","orderWs","relieveWs","wsUserRelieve","scanError","rapidScan"];
    //消息来源  订单=>cOrder:c端指定业务员下单  留言=》branch:申通内部留言   通知=》奖励  投诉理赔=》customer:客户留言（所有品牌） 定时短信=》短信  免费短信=》免费短信领取 代扣订单=》付款  C端扫码签收=》扫码签收  dak:驿站=》'sign':收件,'question':'问题件','pickUp':'取件', 'cooperate':'签约', 'unwind':'解除合作'
    private $MSource = [
                'order'=>['cOrder'],                                                //订单=>cOrder:c端指定业务员下单
                'liuyan'=>['branch', 'customer'],                                   //留言=》branch:申通内部留言
                'notice'=>['reward'],                                               // 通知=》奖励
                'indemnity'=>['customer'],                                          //投诉理赔=》customer:客户留言（所有品牌）
                'smsTiming'=>['sms'],                                               //定时短信=》短信
                'freeSms'=>['sms'],                                                 //免费短信=》免费短信领取
                'finance'=>['payment'],                                             //代扣订单=》付款
                'scanCode'=>['Cscan'],                                              //C端扫码签收=》扫码签收
                'dak'=>['sign','question','pickUp','cooperate', 'unwind' ,'order','pending',"recharge", "cash","unsend", "service"],      //dak:驿站=》'sign':收件,'question':'问题件','pickUp':'取件', 'cooperate':'签约', 'unwind':'解除合作' 'order'=>订单通知快递员,pending=>'待处理' 'recharge' => 驿站充值， 'cash' => 驿站提现 "unsend"=>待揽收 "service"=>服务
                'orderAutoSms'=>['printOrderSmsError'],                            //短信余额不足提醒
                'agreement'=>['apply','relieve'],                                  //微商向快递员协议 apply其他协议   protocol申请协议 relieve解除协议申请 快递员使用
                'WsAgreement'=>['apply','protocol'],                               //微商用户消息
                'printRedPack' =>['new_print_reward'],                             //红包=》红包
                'wsProtocol'=>['protocol'],                                         //二级授权=》 申请授权 拒绝授权 relieve 解除授权
                'pProtocol'=>['protocol','refuse','relieve'],                       //一级操作授权=》 同意授权 拒绝授权 relieve 解除授权
                'orderWs'=>['orderWs'],                                             //微商订单消息
                'relieveWs'=>['relieveWs'],                                         //微商申请解除协议接口
                'wsUserRelieve'=>['relieve'],                                       //微商解除用户授权消息
                'scanError'=>['collect','deliver','sign','question_scan','arrive','from_to'], // '收件'  => 'collect', '派件'  => 'deliver', '签收'  => 'sign', '问题件' => 'question_scan', '到件'  => 'arrive', '发件'  => 'from_to',
                'rapidScan'=>['probation','expire'],                                //急速扫描提醒  "试用" => 'probation' ,"到期"  =>'expire'
    ];

    function __construct(){
//        $this->cache = cache::connect('base');
        $this->cache = cache::connect('base2');
    }
    
    /**
     * 入参校验
     * @param $uid
     * @param $params
     * @param $type
     * @return array|bool
     */
    public function checkParams($uid,$params,$type)
    {
//        临时屏蔽快递员APP消息模块
//        return false;
        $result = [];
        if(!in_array($type,['add','update','del'])){
            return false;
        }
        //公共必传参数
        if(!isset($uid) || empty($uid)){return false;}  //用户id
        if(!isset($params['type']) || empty($params['type']) || !in_array($params['type'],$this->MType)){return false;} //消息类型
        if(!isset($params['id']) || empty($params['id'])){return false;}//消息id

        $params['others'] = empty($params['others'])?['unused'=>'unused']:$params['others'];    //为格式统一，加一个无用字段
        if($type == 'add' || $type == 'update'){
            if(!isset($params['title']) || empty($params['title'])){return false;}//消息标题
            if(!isset($params['dsp']) || empty($params['dsp'])){return false;}//消息描述
            if(!isset($params['time']) || empty($params['time'])){return false;}//消息时间
            if(!isset($params['source']) || empty($params['source']) || !in_array($params['source'],$this->MSource[$params['type']])){return false;}//消息来源
            $result = ['uid'=>$uid,'params'=>$params];
        }elseif($type == 'del'){
            $result = ['uid'=>$uid,'params'=>$params];
        }
        return $result;
    }

    /**
     * 锚点(添加)
     * 功能：新增未处理消息
     * 描述：在所有生成消息的业务节点中使用
     * @param $uid  用户id
     * @param $params   参数
     * @param $type 消息客户端   uid:快递员  wsid 微商
     * @return int|string  1：参数错误  ok：成功
     */
    public function addAnchor($uid,$params,$type="uid")
    {
        //检验消息客户端是否合法
        if(!in_array($type,$this->type)){
            return false;
        }
        $checkResult = $this->checkParams($uid,$params,'add');    //入参校验
        if(empty($checkResult)){
            return false;
        }
        try{
            //取模计算
            $this->key = $this->mod($uid,"untreated:msg");
            $this->hashKey = $type.':'.$checkResult['uid'];   //hash键
            $cashVal = $this->cache->hget($this->key,$this->hashKey);
            if($cashVal == false){  //当前用户无消息
                $data = [$checkResult['params']];
            }else{
                $data = $this->haddleCashData($cashVal,$checkResult['params'],'add'); //组装待写入redis中的数据
            }
            if($cashVal != json_encode($data)){ //若有修改内容，写入，否则不做操作
                sort($data);
                $this->cache->hset($this->key,$this->hashKey,json_encode($data));   //写入redis
//                if(!$this->cache->hset($this->key,$this->hashKey,json_encode($data))){   //写入redis
//                    logDebug(json_encode($data),'untreated:msg','addAnchor',true);
//                }
            }
            unset($uid,$params,$data);
            return 'ok';
        } catch (\Exception $e) {
            logError($e->getMessage(), 'add anchor fail');
        }
    }

    /**
     * 锚点(删除)
     * 功能：删除已处理消息
     * 描述：在所有处理消息的业务节点中使用
     * @param $uid  用户id
     * @param $params   参数
     * @param $type 消息客户端   uid:快递员  wsid 微商
     * @return int|string  1：参数错误  ok：成功
     */
    public function delAnchor($uid,$params,$type='uid')
    {
        //检验消息客户端是否合法
        if(!in_array($type,$this->type)){
            return false;
        }
        $checkResult = $this->checkParams($uid,$params,'del');    //入参校验
        if(empty($checkResult)){
            return false;
        }

        try{
            $this->key = $this->mod($uid,"untreated:msg");
            $this->hashKey = $type.':'.$uid;   //hash键
            $cashVal = $this->cache->hget($this->key,$this->hashKey);
            if($cashVal == false){  //当前用户无消息
                return 'ok';
            }else{
                $data = $this->haddleCashData($cashVal,$params,'del'); //组装待写入redis中的数据
            }
            if($cashVal != json_encode($data)){ //若有修改内容，写入，否则不做操作
                sort($data);
                $this->cache->hset($this->key,$this->hashKey,json_encode($data));   //写入redis
//                if(!$this->cache->hset($this->key,$this->hashKey,json_encode($data))){   //写入redis
//                    logDebug(json_encode($data),'untreated:msg','delAnchor',true);
//                }
            }
            unset($uid,$params,$data);
            return 'ok';
        } catch (\Exception $e) {
            logError($e->getMessage(), 'del anchor fail');
        }
    }


    /**
     * 锚点(更新)
     * 功能：更新未处理消息
     * 描述：在所有生成消息的业务节点中使用
     * @param $uid  用户id
     * @param $params   参数
     * @param $type 消息客户端 uid:快递员  wsid 微商
     * @return int|string  1：参数错误  ok：成功
     */
    public function updateAnchor($uid,$params,$type="uid")
    {
        //检验消息客户端是否合法
        if(!in_array($type,$this->type)){
            return false;
        }
        $checkResult = $this->checkParams($uid,$params,'update');    //入参校验
        if(empty($checkResult)){
            return false;
        }

        try{
            $this->key = $this->mod($uid,"untreated:msg");
            $this->hashKey = $type.':'.$checkResult['uid'];   //hash键
            $time = time();
            $cashVal = $this->cache->hget($this->key,$this->hashKey);
            $time1 = time();
            if(($time1-$time)>2){
                logDebug($this->key.$this->hashKey,"remind_untreated:msg","updateAnchor_hget");
                logDebug(json_encode($cashVal),"remind_untreated:msg","updateAnchor_hget_value",true);
            }
            if($cashVal == false){  //当前用户无消息
                $data = [$checkResult['params']];
            }else{
                $data = $this->haddleCashData($cashVal,$checkResult['params'],'update'); //组装待写入redis中的数据
            }
            if($cashVal != json_encode($data)){ //若有修改内容，写入，否则不做操作
                sort($data);
//                $this->cache->hset($this->key,$this->hashKey,json_encode($data));   //写入redis
                try {
                    $time2 = time();
                    $this->cache->hset($this->key, $this->hashKey, json_encode($data));   //写入redis
                    $time3 = time();
                    if(($time3-$time2)>2){
                        logDebug($this->key.$this->hashKey,"remind_untreated:msg","updateAnchor_hset");
                    }
                } catch (\Exception $e) {
                    logDebug(json_encode($data).$e->getMessage(),'untreated:msg',"updateAnchor",true);
                }
            }
            unset($uid,$params,$data);
            return 'ok';
        } catch (\Exception $e) {
            logError($e->getMessage(), 'update anchor fail');
        }
    }



    /**
     * 根据uid获取用户消息
     * @param $uid
     * @return bool|int|string
     */
    public function getRemindByUid($uid,$type="uid")
    {
//        临时屏蔽快递员APP消息模块查询
//        return [];
        //检验消息客户端是否合法
        if(!in_array($type,$this->type)){
            return false;
        }
        if(empty($uid) || !is_numeric($uid)){
            return false;
        }
        try{
            $this->key = $this->mod($uid,"untreated:msg");
            $this->hashKey = $type.':'.$uid;   //hash键
            $time= time();
            $data = $this->cache->hget($this->key,$this->hashKey);
            $time1= time();
            if(($time1-$time)>2){
                logDebug($this->key.$this->hashKey,"remind_untreated:msg","getRemindByUid");
                logDebug(json_encode($data),"remind_untreated:msg","getRemindByUid",true);
            }
            return $data;
        } catch (\Exception $e) {
            logError($e->getMessage(), 'getRemindByUid fail');
        }
    }

    /**
     * 组装待写入redis中的数据
     * @param $cashVal
     * @param $params
     * @param $operate add:添加未处理消息  del:删除未处理消息
     * @return mixed
     */
    private function haddleCashData($cashVal,$params,$operate)
    {
        try{
            $cashValArr = json_decode($cashVal,true);
            if(json_last_error() == JSON_ERROR_NONE){
                    $isHave = false;
                    foreach ($cashValArr as $rowKey=>$cashValRow){
                        $isHave = ($cashValRow['type'] == $params['type'] && $cashValRow['id'] == $params['id'])?true:false;//是否有重复记录
                        if(($operate == 'del' || $operate == 'update') && $isHave == true){ //删除消息
                            unset($cashValArr[$rowKey]);
                        }
                    }
                    if(($operate == 'add' && $isHave == false) || $operate == 'update'){//添加未处理消息
                        array_push($cashValArr,$params);
                    }
            }
            return $cashValArr;
        } catch (\Exception $e) {
            logError($e->getMessage(), 'haddleCashData fail');
        }
    }
    /**
     * 锚点(删除) 这里是根据用户ID删除
     * 功能：删除用户所有消息
     * 描述：在所有处理消息的业务节点中使用
     * @param $uid  用户id
     * @param $type 消息客户端   uid:快递员  wsid 微商
     * @return int|string  1：参数错误  ok：成功
     */
    public function userRemindDel($uid,$type="uid"){
        $this->key = $this->mod($uid,"untreated:msg");
        $this->hashKey = $type.':'.$uid;   //hash键
        $this->cache->hDel($this->key,$this->hashKey);
        return true;
    }

    /**
     * 增加用户消息数量
     * @param $uid
     */
    public function addUserMsgNum($uid) {
        $hash_key = "user_num_" . $uid;
        if (empty($total_num = $this->cache->hget($hash_key, 'total_num'))) {
            $this->cache->hset($hash_key, 'total_num', 1);
            $this->cache->hset($hash_key, 'read_num', 1);
        } else {
            $this->cache->hset($hash_key, 'total_num', intval($total_num) + 1);
            if (empty($read_num = $this->cache->hget($hash_key, 'read_num'))) {
                $this->cache->hset($hash_key, 'read_num', 1);
            } else
                $this->cache->hset($hash_key, 'read_num', intval($read_num) + 1);
        }
    }

    public function test()
    {
        $this->key = $this->mod('825992',"untreated:msg");
        $this->cache->hset($this->key,'uid:825992','[{"type":"liuyan","title":"留言","id":"521","dsp":"您有新的留言","time":"2017-03-14 10:32:37","source":"branch","others":{"unused":"unused","waybillnum":"568007150032"}},
                                                {"type":"liuyan","title":"留言","id":"514","dsp":"您有新的留言","time":"2017-03-14 10:32:00","source":"branch","others":{"unused":"unused","waybillnum":"568007150032"}},
                                                {"type":"order","title":"新订单","id":"203142223000002","dsp":"您有新的订单","time":"2017-03-14 10:22:30","source":"cOrder","others":{"unused":"unused"}},
                                                {"type":"notice","title":"通知","id":"224031","dsp":"赠送10条免费短信","time":"2017-03-14 20:22:30","source":"reward","others":{"unused":"unused","url":"http://m.kuaidihelp.com/"}},
                                                {"type":"indemnity","title":"投诉理赔","id":"679","dsp":"您有新的理赔","time":"2017-03-14 10:32:31","source":"customer","others":{"unused":"unused"}},
                                                {"type":"agreement","title":"申请签订协议","id":"679","dsp":"微信用户申请与您签订协议！","time":"2017-03-14 10:32:31","source":"apply","others":{"unused":"unused"}}
                                                ]');   //写入redis
        $this->key = $this->mod('2008153',"untreated:msg");
        $this->cache->hset($this->key,'wsid:2008153','[{"type":"WsAgreement","title":"申请授权","id":"679","dsp":"微信用户申请成为您的团队一员","time":"2017-03-14 10:32:31","source":"apply","others":{"unused":"unused"}}
                                                ]');   //写入redis
    }
    
    /**
     * 根据uid 来取模
     * @param type $uid
     * @param type $n 份数
     */
    public function mod($uid,$key='',$n=100){
        $r = $key.'_'.$uid % $n;
        $this->tongji($uid,$r);
        return $r;
    }
    
    
    /**
     * 统计uid的调用情况
     * @param type $uid
     * @param string $key
     */
    private function tongji($uid,$key){
        //取出原来的属性
        $key = $key."_".$uid;
        $r = $this->cache->hget("untreated:msg_tongji",$key);
        $num = 0;
        if(!empty($r)){
            $r = json_decode($r,TRUE);
            $num = intval($r['num']);
            $num++;
            $arr = array('num'=>$num);
        }else{
            $arr = array('num'=>$num);
        }
        $arr = json_encode($arr,JSON_UNESCAPED_UNICODE);
        $this->cache->hset("untreated:msg_tongji",$key,$arr);
        $this->log($uid,array(4053),$num);
    }
    
    /**
     * 统计分析特定uid的上层调用.
     * 
     */
    private function log($uid,$arr,$num){
        //必须在数组里面，根据取模来控制日志写入量
        if(in_array($uid, $arr) && $num % 1000 == 0){
            $html = $this->debug();
            logDebug("uid:".$uid."  ".$html,'untreated:msg_log');
        }
        
    }
    
    private function debug(){
      $array =debug_backtrace();
      unset($array[0]);
      $html = '';
      foreach($array as $row)
        {
           $html .=$row['file'].':'.$row['line'].'行,'.'  调用:'.$row['class'].'->'.$row['function'].'('.json_encode($row['args']).")\n";
        }
        return $html;
    }
    








}